#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "face.h"
#include "customVertex.h"
#include "Game.h"
#include "Constants.h"

// include the Direct3D Library files
//#pragma comment (lib, "d3d9.lib")
//#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;

// function prototypes
void initD3D(HWND hWnd);
void Draw(void);
void cleanD3D(void);
void init_graphics(void);
void init_input(HWND);

// the WindowProc function prototype
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// Init window
bool    initWindow(HINSTANCE hInstance);

HINSTANCE hInst;				// holds the instance for this app
HWND wndHandle;					// global window handle

// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
	// call our function to init and create our window
	if (!initWindow(hInstance))
	{
		MessageBox(NULL, "Unable to create window", "ERROR", MB_OK);
		return false;
	}
 
    initD3D(wndHandle);
	init_input(wndHandle);

    MSG msg;


    while(TRUE)
    {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

			//TODO look at this at some point in time
			if(msg.message == WM_QUIT)
				break;
        }

        if(msg.message == WM_QUIT)
            break;

		Minicraft::Update();
        Draw();
    }

    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;

		case WM_INPUT:
			{
				RAWINPUT InputData;
				UINT DataSize = sizeof(RAWINPUT);
				GetRawInputData((HRAWINPUT)lParam,
								RID_INPUT,
								&InputData,
								&DataSize,
								sizeof(RAWINPUTHEADER));

				Minicraft::MouseMoveX = InputData.data.mouse.lLastX;
				Minicraft::MouseMoveY = InputData.data.mouse.lLastY;

				return 0;
			} break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth = Minicraft::Constants::SCREEN_WIDTH;
    d3dpp.BackBufferHeight = Minicraft::Constants::SCREEN_HEIGHT;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    init_graphics();

    d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting
	d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);    // turn off culling
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer
	
	// disable all texture filtering (we want shitty, pixelated textures!)
	d3ddev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
	d3ddev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
	d3ddev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
}

// register the mouse as a raw input device
void init_input(HWND hWnd)
{
	RAWINPUTDEVICE Mouse;
	Mouse.usUsage = 0x02;
	Mouse.usUsagePage = 0x01;
	Mouse.dwFlags = NULL;
	Mouse.hwndTarget = hWnd;

	RegisterRawInputDevices(&Mouse, 1, sizeof(RAWINPUTDEVICE));
}

// this is the function used to render a single frame
void Draw(void)
{
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

    d3ddev->SetFVF(CUSTOMFVF);

	Minicraft::Draw();

    d3ddev->EndScene(); 

    d3ddev->Present(NULL, NULL, NULL, NULL);
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
	Minicraft::Dispose();
    d3ddev->Release();
    d3d->Release();
}


// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{ 
	Minicraft::Initalize(d3ddev);
}

// Init window
bool initWindow(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	ZeroMemory(&wcex, sizeof(WNDCLASSEX));

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= 0;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= "DirectXExample";
	wcex.hIconSm		= 0;
	RegisterClassEx(&wcex);

	wndHandle = CreateWindow("DirectXExample", 
							 "DirectXExample", 
							 WS_OVERLAPPEDWINDOW,
							 CW_USEDEFAULT, 
							 CW_USEDEFAULT, 
							 640, 
							 480, 
							 NULL, 
							 NULL, 
							 hInstance, 
							 NULL);
   if (!wndHandle)
      return false;
   
   ShowWindow(wndHandle, SW_SHOW);
   UpdateWindow(wndHandle);

   return true;
}